using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Storage;

namespace Toolkit.Utilities
{
    public class EventLogger
    {
        private Dictionary<EventPriority, List<KeyValuePair<DateTime, string>>> eventLog = new Dictionary<EventPriority, List<KeyValuePair<DateTime, string>>>();
        private bool enabled = true;
        private bool logToFile = false;
        private bool archive = false;
        private string logDirectory;
        private string logExtension = "log";
        private List<EventPriority> loggingPriority = new List<EventPriority>();

        private StreamWriter writerLowPriority;
        private StreamWriter writerMediumPriority;
        private StreamWriter writerHighPriority;
        private StreamWriter writerCriticalPriority;

        public Dictionary<EventPriority, List<KeyValuePair<DateTime, string>>> EventLog
        {
            get { return eventLog; }
        }

        public List<KeyValuePair<DateTime, string>> this[EventPriority p]
        {
            get { return eventLog[p]; }
        }

        public bool LogToFile
        {
            get { return logToFile; }
            set { logToFile = value; }
        }

        public string LogDirectory
        {
            get { return logDirectory; }
        }

        public List<EventPriority> LoggingPriority
        {
            get { return loggingPriority; }
            set { loggingPriority = value; }
        }

        public bool Enabled
        {
            get { return enabled; }
            set { enabled = value; }
        }

        public bool Archive
        {
            get { return archive; }
            set { archive = value; }
        }

        public EventLogger()
            : this(false, null, false)
        { }

        public EventLogger(bool logToFile, string logDirectory, bool archive)
        {
            eventLog[EventPriority.Low] = new List<KeyValuePair<DateTime, string>>();
            eventLog[EventPriority.Medium] = new List<KeyValuePair<DateTime, string>>();
            eventLog[EventPriority.High] = new List<KeyValuePair<DateTime, string>>();
            eventLog[EventPriority.Critical] = new List<KeyValuePair<DateTime, string>>();

            loggingPriority.Add(EventPriority.Low);
            loggingPriority.Add(EventPriority.Medium);
            loggingPriority.Add(EventPriority.High);
            loggingPriority.Add(EventPriority.Critical);

#if !XBOX360
            this.logToFile = logToFile;
            this.archive = archive;

            if (logToFile && logDirectory != null)
                SetLogDirectory(logDirectory, false);
#endif
        }

        public void AddEvent(string message, EventPriority priority)
        {
            if (enabled)
            {
                if (loggingPriority.Contains(priority))
                {
                    KeyValuePair<DateTime, string> theEvent = new KeyValuePair<DateTime, string>(DateTime.Now, message);
                    eventLog[priority].Add(theEvent);

#if !XBOX360
                    if (logToFile)
                        WriteEventToFile(theEvent, priority);
#endif
                }
            }
        }

        public void AddEvent(string message)
        {
            AddEvent(message, EventPriority.Low);
        }

        private void WriteEventToFile(KeyValuePair<DateTime, string> theEvent, EventPriority priority)
        {
            StreamWriter writer = null;
            switch (priority)
            {
                case EventPriority.Low:
                    if (writerLowPriority != null)
                        writer = writerLowPriority;
                    break;
                case EventPriority.Medium:
                    if (writerMediumPriority != null)
                        writer = writerMediumPriority;
                    break;
                case EventPriority.High:
                    if (writerHighPriority != null)
                        writer = writerHighPriority;
                    break;
                case EventPriority.Critical:
                    if (writerCriticalPriority != null)
                        writer = writerCriticalPriority;
                    break;
            }
            if (writer != null)
            {
                writer.WriteLine(theEvent.Key + " - " + theEvent.Value);
                writer.Flush();
            }
        }

        public List<string> FindEvent(DateTime time)
        {
            List<string> events = new List<string>();
            foreach (EventPriority eventPriority in eventLog.Keys)
            {
                foreach (KeyValuePair<DateTime, string> logEvent in eventLog[eventPriority])
                {
                    if (logEvent.Key == time)
                        events.Add(logEvent.Value);
                }
            }
            return events;
        }

        public List<KeyValuePair<DateTime, string>> FindEvent(string eventMessage)
        {
            List<KeyValuePair<DateTime, string>> events = new List<KeyValuePair<DateTime, string>>();
            foreach (EventPriority eventPriority in eventLog.Keys)
            {
                foreach (KeyValuePair<DateTime, string> logEvent in eventLog[eventPriority])
                {
                    if (logEvent.Value == eventMessage)
                        events.Add(logEvent);
                }
            }
            return events;
        }

        public void SetSingleLogPriority(EventPriority eventPriority)
        {
            loggingPriority.Clear();
            loggingPriority.Add(eventPriority);
        }

        public void SetLogDirectory(string newLogDirectory, bool moveLogs)
        {
            if (!Directory.Exists(newLogDirectory))
                Directory.CreateDirectory(newLogDirectory);

            if (writerLowPriority != null)
                writerLowPriority.Close();
            if (writerMediumPriority != null)
                writerMediumPriority.Close();
            if (writerHighPriority != null)
                writerHighPriority.Close();
            if (writerCriticalPriority != null)
                writerCriticalPriority.Close();

            if (this.logDirectory != null && moveLogs)
                MoveLogs(newLogDirectory);

            string fileName;

            // Low Priority
            fileName = EventPriority.Low.ToString() + "." + logExtension;
            if (File.Exists(newLogDirectory + "\\" + fileName) && archive && !moveLogs)
                ArchiveFile(newLogDirectory, fileName);
            writerLowPriority = new StreamWriter(newLogDirectory + "\\" + fileName);

            // Medium Priority
            fileName = EventPriority.Medium.ToString() + "." + logExtension;
            if (File.Exists(newLogDirectory + "\\" + fileName) && archive && !moveLogs)
                ArchiveFile(newLogDirectory, fileName);
            writerMediumPriority = new StreamWriter(newLogDirectory + "\\" + fileName);

            // High Priority
            fileName = EventPriority.High.ToString() + "." + logExtension;
            if (File.Exists(newLogDirectory + "\\" + fileName) && archive && !moveLogs)
                ArchiveFile(newLogDirectory, fileName);
            writerHighPriority = new StreamWriter(newLogDirectory + "\\" + fileName);

            // Critical Priority
            fileName = EventPriority.Critical.ToString() + "." + logExtension;
            if (File.Exists(newLogDirectory + "\\" + fileName) && archive && !moveLogs)
                ArchiveFile(newLogDirectory, fileName);
            writerCriticalPriority = new StreamWriter(newLogDirectory + "\\" + fileName);
      
            this.logDirectory = newLogDirectory;
        }

        private void MoveLogs(string newLogDirectory)
        {
            foreach (string file in Directory.GetFiles(this.logDirectory))
            {
                string[] fileSplit = file.Split('\\');
                string fileName = fileSplit[fileSplit.Length - 1];

                if (File.Exists(newLogDirectory + "\\" + fileName))
                {
                    if (archive)
                        ArchiveFile(newLogDirectory, fileName);
                    else
                        File.Delete(newLogDirectory + "\\" + fileName);
                }
                File.Move(file, newLogDirectory + "\\" + fileName);
            }
            Directory.Delete(this.logDirectory);
        }

        private void ArchiveFile(string sourceDirectory, string fileName)
        {
            if (!Directory.Exists(sourceDirectory + "\\Archives"))
                Directory.CreateDirectory(sourceDirectory + "\\Archives");
            File.Move(sourceDirectory + "\\" + fileName, sourceDirectory + "\\Archives\\" + DateTime.Now.ToFileTime() + "_" + fileName);
        }

        [Flags]
        public enum EventPriority
        {
            Low = 0,
            Medium = 1,
            High = 2,
            Critical = 4
        }
    }
}